Spring

Spring IoC
内容 | 说明 | 重要程度 |
---|---|---|
Spring框架介绍 | Spring IoC、DI和AOP等核心概念 | ★★★★★★ |
Spring IoC容器 | Spring实例化与管理对象 | ★★★★★★ |
集合对象注入 | 注入List、Set、Map集合对象 | ★★★★★★ |
底层原理 | Spring Bean的生命周期 | ★★★★★★ |
注解与Java Config | Spring注解分类和常用注解应用 | ★★★★★★ |
Spring AOP
不修改源代码的程序扩展
内容 | 说明 | 重要程度 |
---|---|---|
理解AOP及名词 | Spring AOP开发与配置流程 | ★★★★★★(面试) |
五种通知类型 | Spring五种通知类型与应用场景 | ★★★ |
切点表达式 | PointCut切点表达式的语法规则及应用 | ★★ |
代理模式 | JDK动态代理和CGLib代理的执行过程 | ★★★★★★(面试) |
Spring JDBC与声明式事务
JDBC的扩展
内容 | 说明 | 重要程度 |
---|---|---|
Spring JDBC | Spring JDBC的环境配置 | ★★★★ |
RestTemplate | 基于RestTemplate实现SQL处理 | ★★★ |
配置声明式事务 | 声明式事务的配置过程 | ★★★★★★ |
事务传播行为介绍 | 讲解常用事务传播行为的用途 | ★★★ |
声明式事务注解形式 | 基于注解使用声明式事务 | ★★★★★★ |
★★★★★★ Spring ★★★★★★
IoC容器负责实例化,配置和组装对象。 IoC容器从XML文件获取信息并相应地工作。
IoC容器执行的主要任务是:
- 实例化应用程序类
- 配置对象
- 组装对象之间的依赖关系
有两种类型的IoC容器
BeanFactory
ApplicationContext
IoC控制反转
对象的控制权交给第三方进行管理(中间人)
- IoC控制反转,全称Inverse of Control,是一种设计理念
- 由代理人来创建与管理对象,消费者通过代理人来获取对象
- IoC的目的是降低对象之间直接耦合[更好适合对象之间的变化]
- 加入IoC容器将对象统一管理,让对象关联变为弱耦合
顾客 ←(快递) 果商冷冻仓库 (批发)→ 苹果
DI依赖注入
- IoC是设计理念,是现代程序设计遵循的标准,是宏观目标
- DI(Dependency Injection)是具体技术实现,是微观实现
- DI在Java中利用反射技术实现对象注入(Injection) [不同语言运用不同技术]
Spring含义
- Spring可以从广义和狭义两个角度看待
- 广义的Spring是指Spring生态系统
- 狭义的Spring是指Spring框架(Spring Framework)
广义的Spring生态体系
- 分布式微服务 SpringCloud
- Reactive相应服务
- Web apps 是 SpringMVC 中的
- Serverless 无服务器内容
- Event Driven 和 Batch
- Spring Framework Spring Boot Spring Cloud Spring Cloud Data Flow…
狭义的Spring框架
- Spring框架是企业开发复杂性的一站式解决方案
- Spring框架的核心是IoC容器与AOP面向切面编程
- Spring IoC负责创建与管理系统对象,并在此基础上扩展功能(不修改源代码)
传统开发方式
- 对象直接引用导致对象硬性关联,程序难以扩展维护 new B new A
Spring IoC容器
IoC容器是Spring生态的地基,用于统一创建于管理对象依赖
使用者直接提取Spring IoC容器中的已经将依赖ObjectB注入到ObjectA 直接提取A
Spring IoC容器职责 [宏观理念]
- 对象的控制权交由第三方统一管理 (IoC控制反转)
- 利用Java反射技术实现运行时对象创建与关联 (DI依赖注入) [技术实现]
- 基于配置提高应用程序的可维护性与扩展性
Spring IoC初体验
三个小孩吃三种不同的苹果
原始代码
Apple.java
public class Apple {
private String title;
private String color;
private String origin;
Construct(空+满) Getter+Setter
}
Child.java
public class Child {
private String name;
private Apple apple;
Construct(空+满) Getter+Setter
public void eat(){
System.out.println(name + "吃到了" + apple.getOrigin() + "种植的" + apple.getTitle());
}
}
Application.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Apple;
import com.imooc.spring.ioc.entity.Child;
public class Application {
public static void main(String[] args) {
Apple apple1 = new Apple("红富士", "红色", "欧洲");
Apple apple2 = new Apple("青苹果", "绿色", "中亚");
Apple apple3 = new Apple("红富士", "红色", "欧洲");
Child lily = new Child("莉莉",apple1);
Child andy = new Child("安迪",apple2);
Child luna = new Child("露娜",apple3);
lily.eat();
andy.eat();
luna.eat();
}
}
Spring IoC方式代码[不修改源代码 不用new 引入容器让对象统一管理]
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Spring_test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
applicationContext.xml [静态信息可以放其中] **属性
**元素的 ref 属性用于定义另一个bean的引用。
SpringIoC核心配置文件 右键resources创建 所有对象的创建以及关联的设置都是在applicationContext.xml这里进行
Spring.io -> Project -> Spring Framework -> LEARN -> Reference Doc. -> Core
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
并且加入配置Spring[出现在提示页面]
------------------------ 更新一次 ------------------------
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 在IoC容器启动时,自动由Spring实例化Apple对象,取名sweetApple放入到容器中 -->
<bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="origin" value="欧洲"></property>
<property name="color" value="红色"></property>
</bean>
<bean id="sourApple" class="com.imooc.spring.ioc.entity.Apple">
<property name="title" value="青苹果"></property>
<property name="origin" value="中亚"></property>
<property name="color" value="绿色"></property>
</bean>
<bean id="softApple" class="com.imooc.spring.ioc.entity.Apple">
<property name="title" value="沙果"></property>
<property name="origin" value="中国"></property>
<property name="color" value="黄色"></property>
</bean>
<bean id="rdApple" class="com.imooc.spring.ioc.entity.Apple">
<property name="title" value="蛇果"></property>
<property name="origin" value="美国"></property>
<property name="color" value="红色"></property>
</bean>
<bean id="lily" class="com.imooc.spring.ioc.entity.Child">
<property name="name" value="莉莉"/>
<property name="apple" ref="sweetApple"/>
</bean>
<bean id="andy" class="com.imooc.spring.ioc.entity.Child">
<property name="name" value="安迪"/>
<property name="apple" ref="sourApple"/>
</bean>
<bean id="luna" class="com.imooc.spring.ioc.entity.Child">
<property name="name" value="露娜"/>
<property name="apple" ref="rdApple"/>
</bean>
</beans>
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Apple;
import com.imooc.spring.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Apple sweetApple = context.getBean("sweetApple", Apple.class);
System.out.println(sweetApple.getTitle());
//从IoC容器中提取beanId=lily的对象
Child lily = context.getBean("lily", Child.class);
lily.eat();
Child andy = context.getBean("andy", Child.class);
andy.eat();
Child luna = context.getBean("luna", Child.class);
luna.eat();
}
}
————————————————————————————————————————————————————————————————————————————————————
红富士
莉莉吃到了欧洲种植的红富士
安迪吃到了中亚种植的青苹果
露娜吃到了美国种植的蛇果
利用IoC容器有利于对象与对象之间的解耦 springIoC大大提高了程序的维护与延展
XML管理对象(Bean)
- 基于XML配置Bean
上述的吃苹果例题就是基于xml配置Bean
- 基于注解配置Bean
- 基于Java代码配置Bean(java .config)
applicationContext.xml
<bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
<property name="title" value="红富士"></property>
<property name="origin" value="欧洲"></property>
<property name="color" value="红色"></property>
</bean>
所有都要用bean标签 SpringIoC实例化以后在容器的唯一编号: id="sweetAppele" SpringIoC就知道创建IoC容器时实例化一个Apple对象同时bean id="..."
XML方式创建IoC容器
//创建IoC容器并根据配置文件创建对象
ApplicationContext context = new
ClassPathXmlApplicationContext("classpath:applicationContext.xml");
实例化Bean的三种方式
- 基于构造方法对象实例化 (90%以上)
利用构造方法参数名实例化 [推荐]
<bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
<!-- 没有constructor-arg则代表调用默认构造方法实例化 -->
<constructor-arg name="title" value="红富士"/>
<constructor-arg name="origin" value="欧洲"/>
<constructor-arg name="color" value="红色"/>
</bean>
Apple.java
public class Apple {
private String title;
private String color;
private String origin;
public Apple() {
System.out.println("Apple对象已创建," + this);
}
public Apple(String title, String color, String origin) {
System.out.println("通过带参构造方法创建对象" + this);
this.title = title;
this.color = color;
this.origin = origin;
}
......
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Apple;
import com.imooc.spring.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
}
___________________________________________________________________________
Apple对象已创建,com.imooc.spring.ioc.entity.Apple@39fb3ab6
通过带参构造方法创建对象com.imooc.spring.ioc.entity.Apple@1a968a59
利用构造方法参数位置实例化
<bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
<!-- 利用构造方法参数位置实现对象实例化 -->
<constructor-arg index="0" value="红富士"/>
<constructor-arg index="1" value="欧洲"/>
<constructor-arg index="2" value="红色"/>
</bean>
AppleStaticFactory.java
package com.imooc.spring.ioc.factory;
import com.imooc.spring.ioc.entity.Apple;
public class AppleStaticFactory {
public static Apple createSweetApple(){ //静态工厂 用于创建对象的方法是静态的
Apple apple = new Apple();
apple.setTitle("红富士");
apple.setOrigin("欧洲");
apple.setColor("红色");
return apple;
}
}
applicationContext.java
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--利用静态工厂获取对象-->
<bean id="apple4" class="com.imooc.spring.ioc.factory.AppleStaticFactory"
factory-method="createSweetApple"/>
</beans>
<bean id="a" class="com.nhooo.A" factory-method="getA"></bean>
A.java
package com.nhooo;
public class A {
private static final A obj=new A();
private A(){System.out.println("private constructor");}
public static A getA(){
System.out.println("factory method ");
return obj;
}
public void msg(){
System.out.println("hello user");
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="a" class="com.nhooo.A" factory-method="getA"></bean>
</beans>
Test.java
package org.sssit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
A a=(A)context.getBean("a");
a.msg();
}
}
=============================================================
private constructor
factory method
hello user
AppleFactoryInstance.java
package com.imooc.spring.ioc.factory;
import com.imooc.spring.ioc.entity.Apple;
/**
* 工厂实例方法创建对象是指IoC容器对工厂类进行实例化并调用对应的实例方法创建对象的过程
*/
public class AppleFactoryInstance {
public Apple createSweetApple(){ //静态工厂 用于创建对象的方法是静态的
Apple apple = new Apple();
apple.setTitle("红富士");
apple.setOrigin("欧洲");
apple.setColor("红色");
return apple;
}
}
applicationContext.java
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--利用工厂实例方法获取对象-->
<bean id="factoryInstance" class="com.imooc.spring.ioc.factory.AppleFactoryInstance"/>
<bean id="apple5" factory-bean="factoryInstance" factory-method="createSweetApple"/>
</beans>
SpringApplication.java
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
}
从IoC容器获取bean
Apple sweetApple = context.getBean("sweetApple",Apple.class); 【推荐】
或者
Apple sweetApple = (Apple)context.getBean("sweetApple");
-------------------------------------------------------------
System.out.println(sweetApple.getTitle());
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Apple apple4 = context.getBean("apple4", Apple.class); 【推荐】
System.out.println(apple4.getTitle());
Apple apple3 = (Apple)context.getBean("apple3");
System.out.println(apple3.getTitle());
}
}
id与name属性相同点
- bean id 与 name 都是设置对象在IoC容器中唯一标识
<bean id="apple3" class="com.imooc.spring.ioc.entity.Apple">
<bean name="apple3" class="com.imooc.spring.ioc.entity.Apple">
- 两者在同一个配置文件中都不允许出现重复
- 两者允许在多个配置文件中出现重复,新对象覆盖旧对象
- id要求更为严格,一次只能定义一个对象标识 【推荐】
- name更为宽松,一次允许定义多个对象标识
- tips: id与name的命名要求有意义,按驼峰命名书写
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple3" class="com.imooc.spring.ioc.entity.Apple">
<!-- 利用构造方法参数位置实现对象实例化 -->
<constructor-arg index="0" value="红富士"/>
<constructor-arg index="1" value="欧洲"/>
<constructor-arg index="2" value="红色"/>
<constructor-arg index="3" value="19.8"/>
</bean>
</beans>
applicationContext-1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="apple2" class="com.imooc.spring.ioc.entity.Apple">
<!-- 利用构造方法参数位置实现对象实例化 -->
<constructor-arg name="title" value="红富士2号"/>
<constructor-arg name="origin" value="欧洲"/>
<constructor-arg name="color" value="红色"/>
<constructor-arg name="price" value="19.8"/>
</bean>
</beans>
SpringApplication.java
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml","classpath:applicationContext-1.xml");
Apple apple2 = context.getBean("apple2",Apple.class);
System.out.println(apple2.getTitle());
Apple apple3 = context.getBean("apple3",Apple.class);
System.out.println(apple3.getTitle());
}
}
______________________________________________________________________
红富士2号
红富士
< bean name = “apple2, apple7” class=”com.imooc.spring.ioc.entity.Apple” >
如果使用name(不使用id)可以在后面增加标识名
在没有id与name的bean默认使用类名全称作为bean标识
< bean class=”com.imooc.spring.ioc.entity.Apple” >
路径匹配表达式
加载单个配置文件
//创建IoC容器并根据配置文件创建对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
加载多配置文件
String[] configLocations = new String[]{"classpath:applicationContext.xml","classpath:applicationContext-1.xml"};
ApplicationContext context = new ClassPathXmlApplicationContext(configLocations);
路径表达式 (config.xml = ApplicationContext.xml)
表达式实例 | 说明 |
---|---|
classpath:config.xml | 扫描classpath根路径(不包含jar)的config.xml |
classpath:com/imooc/config.xml | 扫描classpath下(不包含jar)com.imooc包中的config.xml |
classpath*:com/imooc/config.xml | 扫描classpath下(包含jar)com.imooc包中的config.xml |
classpath:config-*.xml | 扫描classpath根路径下所有以config-开头的XML文件 |
classpath:com/**/config.xml | 扫描com包下(包含任何子包)的config.xml |
file:c:/config.xml | 扫描c盘根路径config.xml |
对象依赖注入
- 依赖注入是指运行时将容器内对象利用反射赋給其他对象的操作
- 基于setter方法注入对象
利用setter实现静态数值注入
<bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
<!-- IoC容器自动利用反射机制运行时调用setXXX方法为属性赋值 -->
<property name="title" value="红富士"/>
<property name="color" value="红色"/>
<property name="origin" value="欧洲"/>
<property name="price" value="19.8"/>
</bean>
利用setter实现对象注入 【核心操作 ref=””】
**属性
**元素的 ref 属性用于定义另一个bean的引用。
<bean id="lily" class="com.imooc.spring.ioc.entity.Child">
<property name="name" value="莉莉"/>
<!-- 利用ref注入依赖对象 -->
<property name="apple" ref="sweetApple"/>
</bean>
Child.java #创建好的apple对象赋予給参数进入setApple
public void setApple(Apple apple) {
System.out.println("注入的Apple对象:" + apple);
this.apple = apple;
}
SpringApplication.java
public class SpringApplication {
public static void main(String[] args) {
//创建Spring IoC容器,并根据配置文件在容器中实例化对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml","classpath:applicationContext-1.xml");
Apple sweetApple = context.getBean("sweetApple", Apple.class);
System.out.println(sweetApple.getTitle());
}
}
体验依赖注入的优势 (高效解耦)
applicatioinContext-dao.xml #用于数据库的增删改查
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.imooc.spring.ioc.bookshop.dao.BookDaoImpl">
//如果更改了数据库类型 只需要重新创建一个java实现接口BookDao的insert 之后只需更改class="com.imooc.spring.ioc.bookshop.dao.BookDaoOracleImpl"
</bean>
</beans>
applicatioinContext-service.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookService" class="com.imooc.spring.ioc.bookshop.service.BookService">
<!-- id=bookDao -->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
BookDao.java
package com.imooc.spring.ioc.bookshop.dao;
public interface BookDao {
public void insert();
}
BookDaoImpl.java
package com.imooc.spring.ioc.bookshop.dao;
public class BookDaoImpl implements BookDao{
@Override
public void insert() {
System.out.println("向mysql book表插入数据");
}
}
BookService.java
package com.imooc.spring.ioc.bookshop.service;
import com.imooc.spring.ioc.bookshop.dao.BookDao;
public class BookService {
private BookDao bookDao; //接口将在ioc启动的时候动态注入
public void purchase(){
System.out.println("正在执行图书采购业务方法");
bookDao.insert();
}
public BookDao getBookDao(){
return bookDao;
}
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
}
BookShopApplication.java
package com.imooc.spring.ioc.bookshop;
import com.imooc.spring.ioc.bookshop.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BookShopApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-*.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.purchase();
}
}
利用构造方法实现对象依赖注入
对象依赖注入
- 依赖注入是指运行时将容器内对象利用反射赋給其他对象的操作
- 基于setter方法注入对象
- 基于构造方法注入对象
【com.imooc.spring.ioc.entity】
Child.java + Apple.java
Apple.java中可以加一个
public void apple(){
System.out.println(origin+"国家" +color+"的"+title+"食物");
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="sweetApple" class="com.imooc.Apple.entity.Apple">
<property name="title" value="青苹果"/>
<property name="color" value="绿色"/>
<property name="origin" value="中亚"/>
</bean>
<bean id="lily" class="com.imooc.Apple.entity.Child">
<property name="name" value="莉莉"/>
<property name="apple" ref="sweetApple"/>
</bean>
</beans>
package com.imooc.Apple;
import com.imooc.Apple.entity.Apple;
import com.imooc.Apple.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext-*.xml");
Apple sweetApple = context.getBean("sweetApple", Apple.class);
Child lily = context.getBean("lily", Child.class);
// 添加打印语句,检查属性是否正确注入
System.out.println("lily's name: " + lily.getName());
System.out.println("lily's apple origin: " + lily.getApple().getOrigin());
System.out.println("lily's apple title: " + lily.getApple().getTitle());
System.out.println("====================================");
// System.out.println(sweetApple.getTitle()+sweetApple.getColor()+sweetApple.getOrigin());
lily.eat();
sweetApple.apple();
}
}
注入集合对象_1
注入List [允许重复数据]
<bean id = "..." class = "...">
<property name = "someList">
<list>
<value>具体值</value>
<ref bean="beanId"></ref>
</list>
</property>
</bean>
注入Set [不允许重复数据 自动去除重复]
<bean id = "..." class = "...">
<property name = "someSet">
<set>
<value>具体值</value>
<ref bean="beanId"></ref>
</set>
</property>
</bean>
注入Map
<bean id = "..." class = "...">
<property name = "someMap">
<Map>
<entry key="k1" value="v1"></entry> #静态数值
<entry key="k2" value-ref="beanId"></entry> #对象引用
</Map>
</property>
</bean>
注入Properties
<bean id = "..." class = "...">
<property name = "someProperties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</property>
</bean>
公司资产配置清单[小案例]
constructor-arg:通过 构造函数注入 。
property:通过 setter对应的方法注入 。
Company.java
package com.imooc.spring.ioc.entity;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Company {
private List<String> rooms;
private Map<String,Computer> computers; //每一条数据保存的都是Computer型
private Properties info;//键值对的写
@Override
public String toString() {
return "Company{" +
"rooms=" + rooms +
", computers=" + computers +
", info=" + info +
'}';
}
+ Getter Setter
}
Computer.java
public class Computer {
private String brand;
private String type;
private String sn;
private Float price;
constructor(空+满)+Getter Setter
}
如何在ioc容器创建后自动的实例化Company对象并且填充信息呢?
所有的工作都在applicationContext.xml
中进行的
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="company" class="com.imooc.spring.ioc.entity.Company">
<property name="rooms">
<list> //【允许重复】
<value>2001-总裁办</value>
<value>2003-总经理办公室</value>
<value>2010-研发部会议室</value>
</list>
</property>
</bean>
</beans>
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Company;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Company company = context.getBean("company", Company.class);
System.out.println(company);
}
}
------------------------------------------------------------
Company{rooms=[2001-总裁办, 2003-总经理办公室, 2010-研发部会议室], computers=null, info=null}
Process finished with exit code 0
------------------------------------------------------------
下方xml更新后的显示数据:
Company{rooms=[2001-总裁办, 2003-总经理办公室, 2010-研发部会议室], computers={dev-88172=Computer{brand='联想', type='台式机', sn='8389283012', price=3085.0}, dev-88173=Computer{brand='联想', type='台式机', sn='8389283012', price=3085.0}}, info={phone=010-12345678, address=北京市朝阳区XX路XX大厦, website=https://p-luminary.github.io}}
https://p-luminary.github.io
Process finished with exit code 0
list的底层是ArrayList
Set的底层是LinkedHashSet [双向有序列表]
Map的底层是LinkedHashMap [双向列表 提取也是存放顺序]
applicationContext.xml [更新一次]
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="c1" class="com.imooc.spring.ioc.entity.Computer">
<constructor-arg name="brand" value="联想"/>
<constructor-arg name="type" value="台式机"/>
<constructor-arg name="sn" value="8389283012"/>
<constructor-arg name="price" value="3085"/>
</bean>
<bean id="company" class="com.imooc.spring.ioc.entity.Company">
<property name="rooms">
<set>
<value>2001-总裁办</value>
<value>2003-总经理办公室</value>
<value>2010-研发部会议室</value>
<value>2010-研发部会议室</value>
</set>
</property>
<property name="computers">
<map>
<entry key="dev-88172" value-ref="c1"/> <!-- 对象引用c1在上方 -->
</map>
</property>
</bean>
</beans>
或者【简便方法】↓
applicationContext.xml 更新二次【新增简便方法】
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="c1" class="com.imooc.spring.ioc.entity.Computer">【方法一】
<constructor-arg name="brand" value="联想"/>
<constructor-arg name="type" value="台式机"/>
<constructor-arg name="sn" value="8389283012"/>
<constructor-arg name="price" value="3085"/>
</bean>
<bean id="company" class="com.imooc.spring.ioc.entity.Company">
<property name="rooms">
<set>
<value>2001-总裁办</value>
<value>2003-总经理办公室</value>
<value>2010-研发部会议室</value>
<value>2010-研发部会议室</value>
</set>
</property>
<property name="computers">
<map>
<entry key="dev-88172" value-ref="c1"/> <!-- 对象引用c1在上方 -->
<entry key="dev-88173">【方法二 (推荐)】
<bean class="com.imooc.spring.ioc.entity.Computer">
<constructor-arg name="brand" value="联想"/>
<constructor-arg name="type" value="台式机"/>
<constructor-arg name="sn" value="8389283012"/>
<constructor-arg name="price" value="3085"/>
</bean>
</entry>
</map>
</property>
<property name="info">
<props>
<prop key="phone">010-12345678</prop>
<prop key="address">北京市朝阳区XX路XX大厦</prop>
<prop key="website">https://p-luminary.github.io</prop>
</props>
</property>
</bean>
</beans>
查看容器内对象 【.getBeanDefinitionNames()】
多个同类型的bean会自动增加编号 #1 #2
<bean class="com.imooc.spring.ioc.entity.Computer">
<constructor-arg name="brand" value="微星"/>
<constructor-arg name="type" value="台式机"/>
<constructor-arg name="sn" value="8389283012"/>
<constructor-arg name="price" value="3000"/>
</bean>
<bean class="com.imooc.spring.ioc.entity.Computer">
<constructor-arg name="brand" value="华硕"/>
<constructor-arg name="type" value="台式机"/>
<constructor-arg name="sn" value="9023283012"/>
<constructor-arg name="price" value="5600"/>
</bean>
//获取容器内所有beanId数组
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName:beanNames){
System.out.println(beanName); //c1 company ...<bean id="...">
System.out.println("类型:" + context.getBean(beanName).getClass().getName());
System.out.println("内容:" + context.getBean(beanName).toString());
}
Computer computer = context.getBean("com.imooc.spring.ioc.entity.Computer", Computer.class);
System.out.println(computer.getBrand());
Computer computer1 = context.getBean("com.imooc.spring.ioc.entity.Computer#1", Computer.class);
System.out.println(computer1.getBrand());
bean scope属性
- bean scope属性用于决定对象何时被创建与作用范围
- bean scope配置将影响容器内对象的数量
- bean scope默认值singleton(单例), 指全局共享同一个对象实例
scope用法
<bean id="bookDao"
class="com.imooc.spring.ioc.bookshop.dao.BookDaoOracleImpl"
scope="prototype"/>
bean scope属性清单
scope属性 | 说明 |
---|---|
singleton | 单例(默认值),每一个容器有且只有唯一的实例,实例被全局共享 |
prototype | 多例,每次使用时都是创建一个实例 |
request | web环境下,每一个独立请求存在唯一实例 |
session | web环境下,每一个session存在有唯一实例 |
application | web环境下,ServletContext存在唯一实例 |
websocket | 每一次WebSocket连接中存在唯一实例 |
singleton与prototype对比
singleton | prototype | |
---|---|---|
对象数量 | 全局唯一 | 存在多个 |
实例化时机 | IoC容器启动时 | getBean() 或 对象注入时 |
线程安全问题 | 存在 | 不存在 |
执行效率 | 高 | 低 |
UserDao.java
package com.imooc.spring.ioc.dao;
public class UserDao {
public UserDao(){
System.out.println("UserDao已创建: " + this);
}
}
UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
public class UserService {
public UserDao userDao;
public UserService() {
System.out.println("UserService已创建: " + this);
}
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
System.out.println("调用setUserDao: " + userDao);
this.userDao = userDao;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.imooc.spring.ioc.dao.UserDao" scope="prototype"/> <!-- 多例 -->
<bean id="userService" class="com.imooc.spring.ioc.service.UserService" scope="prototype"> <!-- 单例 创建一个对象 -->
<property name="userDao" ref="userDao"/> <!-- 引用了上面的userDao多例 再创建一个对象 -->
</bean>
</beans>
SpringApplication.java 【单例】
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserDao userDao = context.getBean("userDao", UserDao.class);
UserDao userDao1 = context.getBean("userDao", UserDao.class);
UserDao userDao2 = context.getBean("userDao", UserDao.class);
UserDao userDao3 = context.getBean("userDao", UserDao.class);
}
}
applicationContext.xml
<bean id="userDao" class="com.imooc.spring.ioc.dao.UserDao" scope="prototype"/> <!-- 多例 -->
<bean id="userService" class="com.imooc.spring.ioc.service.UserService" > <!-- 单例 创建一个对象 -->
<property name="userDao" ref="userDao"/> <!-- 引用了上面的userDao多例 再创建一个对象 -->
</bean>
================================================================
输出:
UserService已创建: com.imooc.spring.ioc.service.UserService@3dd3bcd
UserDao已创建: com.imooc.spring.ioc.dao.UserDao@7c16905e
调用setUserDao: com.imooc.spring.ioc.dao.UserDao@7c16905e
Process finished with exit code 0
SpringApplication.java 【多例】
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("IoC容器已初始化");
UserService userService1 = context.getBean("userService", UserService.class);
UserService userService2 = context.getBean("userService", UserService.class);
UserService userService3 = context.getBean("userService", UserService.class);
UserService userService4 = context.getBean("userService", UserService.class);
}
}
applicationContext.xml
<bean id="userDao" class="com.imooc.spring.ioc.dao.UserDao" scope="prototype"/> <!-- 多例 -->
<bean id="userService" class="com.imooc.spring.ioc.service.UserService" scope="prototype"> <!-- 单例 创建一个对象 -->
<property name="userDao" ref="userDao"/> <!-- 引用了上面的userDao多例 再创建一个对象 -->
</bean>
================================================================
输出:
IoC容器已初始化
UserService已创建: com.imooc.spring.ioc.service.UserService@5c7fa833
UserDao已创建: com.imooc.spring.ioc.dao.UserDao@4dfa3a9d
调用setUserDao: com.imooc.spring.ioc.dao.UserDao@4dfa3a9d
UserService已创建: com.imooc.spring.ioc.service.UserService@4b952a2d
UserDao已创建: com.imooc.spring.ioc.dao.UserDao@3159c4b8
调用setUserDao: com.imooc.spring.ioc.dao.UserDao@3159c4b8
UserService已创建: com.imooc.spring.ioc.service.UserService@73846619
UserDao已创建: com.imooc.spring.ioc.dao.UserDao@4bec1f0c
调用setUserDao: com.imooc.spring.ioc.dao.UserDao@4bec1f0c
UserService已创建: com.imooc.spring.ioc.service.UserService@29ca901e
UserDao已创建: com.imooc.spring.ioc.dao.UserDao@5649fd9b
调用setUserDao: com.imooc.spring.ioc.dao.UserDao@5649fd9b
Process finished with exit code 0
一般来说dao类 service类 control类都是单例 因为单例安全根源是运行时发生不断的变化 如果在真正环境中一般不会重新设置那些类,在运行中都是恒定不变的。所以推荐用单例默认singleton
对象生命周期
Order.java
package com.imooc.spring.ioc.entity;
public class Order {
private Float price;
private Integer quantity;
private Float total;
public Order() {
System.out.println("创建Order对象" + this);
}
public void init(){
System.out.println("执行init()方法");
total = price * quantity;
}
public void pay(){
System.out.println("订单金额为:" + total);
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
System.out.println("设置price:" + price);
this.price = price;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
System.out.println("设置quantity:" + quantity);
this.quantity = quantity;
}
public Float getTotal() {
return total;
}
public void setTotal(Float total) {
this.total = total;
}
public void destroy(){
System.out.println("释放与订单对象相关的资源");
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.imooc.spring.ioc.dao.UserDao" scope="prototype"/> <!-- 多例 -->
<bean id="userService" class="com.imooc.spring.ioc.service.UserService" scope="prototype"> <!-- 单例 创建一个对象 -->
<property name="userDao" ref="userDao"/> <!-- 引用了上面的userDao多例 再创建一个对象 -->
</bean>
<bean id="order1" class="com.imooc.spring.ioc.entity.Order" init-method="init" destroy-method="destroy">
<property name="price" value="19.8"/>
<property name="quantity" value="1000"/>
</bean>
</beans>
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Order;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("======IoC容器已初始化======");
Order order1 = context.getBean("order1",Order.class);
order1.pay();
((ClassPathXmlApplicationContext)context).registerShutdownHook(); //销毁容器 自动调用bean中设置的destory方法
}
}
==========================================
创建Order对象com.imooc.spring.ioc.entity.Order@153f5a29
设置price:19.8
设置quantity:1000
执行init()方法
======IoC容器已初始化======
订单金额为:19800.0
释放与订单对象相关的资源
Process finished with exit code 0
实现极简IoC容器 [利用反射机制完成对象的创建和注入]
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Spring_test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- Dom4j是Java的XML解析组件-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<!-- jaxen是Xpath表达式解释器-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
Apple.java
public class Apple {
private String title;
private String color;
private String origin;
} + Getter Setter
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "sweetApple" class="com.imooc.spring.ioc.context.Apple">
<property name="title" value="红富士"/> //这里的title是在setter中 public void setTitle(String title){this.title=title;}
<property name="color" value="红色"/>
<property name="origin" value="欧洲"/>
</bean>
</beans>
ApplicationContext.java[接口]
package com.imooc.spring.ioc.context;
public interface ApplicationContext {
public Object getBean(String beanId);
}
ClassPathXmlApplicationContext.java
package com.imooc.spring.ioc.context;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext{
private Map iocContainer = new HashMap();
public ClassPathXmlApplicationContext(){
try {
String filePath = this.getClass().getResource("/applicationContext.xml").getPath();
filePath = new URLDecoder().decode(filePath,"UTF-8");
SAXReader reader = new SAXReader();
Document document = reader.read(new File(filePath));
List<Node> beans = document.getRootElement().selectNodes("bean");//得到节点的集合
for (Node node:beans){
Element ele = (Element) node;
String id = ele.attributeValue("id");
String className = ele.attributeValue("class");
Class c = Class.forName(className);//加载指定类
Object obj = c.newInstance();
List<Node> properties = ele.selectNodes("property");
for (Node p:properties){
Element property = (Element) p;
String propName = property.attributeValue("name");
String propValue= property.attributeValue("value");
//基于property完成注入是通过Setter的set方法,set方法命名的格式为setTitle 属性名第一个字母有个大写
String setMethodName = "set" + propName.substring(0,1).toUpperCase()+propName.substring(1);
System.out.println("准备执行" + setMethodName + "方法注入数据");
Method setMethod = c.getMethod(setMethodName, String.class);
setMethod.invoke(obj, propValue); //执行哪个对象的实例方法
}
iocContainer.put(id,obj); //赋予了bean id
}
System.out.println(iocContainer);
System.out.println("IoC容器初始化完毕");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Object getBean(String beanId) {
return iocContainer.get(beanId);
}
}
Application.java
package com.imooc.spring.ioc.context;
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext();
Apple apple = (Apple) context.getBean("sweetApple");
System.out.println(apple);
}
}
=================================================
准备执行setTitle方法注入数据
准备执行setColor方法注入数据
准备执行setOrigin方法注入数据
{sweetApple=com.imooc.spring.ioc.context.Apple@484b61fc}
IoC容器初始化完毕
com.imooc.spring.ioc.context.Apple@484b61fc
Process finished with exit code 0
四种组件类型注解
基于注解配置IoC容器
基于注解的优势
- 摆脱繁琐的XML形式的bean与依赖注入配置
- 基于”声明式”的原则,更适合轻量级的现代企业应用
- 让代码可读性变得更好,研发人员拥有更好的开发体验
三类注解
- 四种组件类型注解-声明当前类的功能与职责
注解 | 说明 |
---|---|
@Component | 组件注解,通用注解,被该注解描述的类将被IoC容器管理并实例化 |
@Controller | 语义注解,说明当前类是MVC应用中的控制器类 |
@Service | 语义注解,说明当前类是Service业务服务类 |
@Repository | 语义注解,说明当前类用于业务持久层,通常描述对应Dao类 |
开启组件扫描
XML配置开启组件扫描,才能使用注解
<context:component-scan base-package="com.imooc">
<context:exclude-filter type="regex" expression="com.imooc.exl.*"/>
</context:component-scan>
- 自动装配注解-根据属性特征自动注入对象
- 元数据注解-更细化的辅助IoC容器管理对象的注解
基于注解初始化IoC容器
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository
@Service
@Controller
@Component
-->
<context:component-scan base-package="com.imooc"/> //设置基准的包名去扫描
</beans>
controller/UserController.java
package com.imooc.spring.ioc.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
dao/UserDao.java
package com.imooc.spring.ioc.dao;
import org.springframework.stereotype.Repository;
//组件类型解释默认beanId为类名首字母小写
//组件类型解释默认beanId为类名首字母小写
//beanId = userDao
@Repository
public class UserDao {
}
service/UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}
utils/StringUtils.java
package com.imooc.spring.ioc.utils;
import org.springframework.stereotype.Component;
@Component
public class StringUtils {
}
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Order;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
String[] ids = context.getBeanDefinitionNames();
for (String id:ids){ //这些bean在容器中是单例,在初始化的时候创建对象
System.out.println(id+":"+context.getBean(id));
}
}
}
==============================================================
//组件类型解释默认beanId为类名首字母小写
userController:com.imooc.spring.ioc.controller.UserController@4f51b3e0
userDao:com.imooc.spring.ioc.dao.UserDao@4b9e255
userService:com.imooc.spring.ioc.service.UserService@5e57643e
stringUtils:com.imooc.spring.ioc.utils.StringUtils@133e16fd
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@51b279c9
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@1ad282e0
org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@7f416310
org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@1cab0bfb
org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@5e955596
Process finished with exit code 0
自动装配与Autowired注解
两类自动装配注解 [@Primary 如果有两个写此注解作为主要]
分类 | 注解 | 说明 |
---|---|---|
按类型装配 | @Autowired | 按容器内对象类型动态注入属性,由Spring机构提供 |
@Inject | 基于JSR-330标准,其他同@Autowired,但不支持required属性 | |
按名称装配 | @Named | 与@Inject配合使用,JSR-330规范,按属性名自动装配属性 |
@Resource | 基于JSR-250规范,优先按名称,再按类型智能匹配 |
模式 | 说明 |
---|---|
no | 这是默认的自动装配模式,意味着默认情况下没有自动装配 |
byName | byName模式根据bean的名称注入对象依赖项。在这种情况属性名称和bean名称必须相同,它在内部调用setter方法 |
byType | byType模式根据类型注入对象依赖项,因此属性名称和bean名称可以不同,它在内部调用setter方法 |
constructor | 构造函数模式通过调用类的构造函数来注入依赖项。它会调用具有大量参数的构造函数。 |
autodetect | 从Spring 3开始不推荐使用 |
无法确认注入哪个bean 解决方案【UserDao 与 UserOracleDao】
UserDao.java
package com.imooc.spring.ioc.dao;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
//组件类型解释默认beanId为类名首字母小写
//beanId = userDao
@Repository
public class UserDao implements IUserDao{
public UserDao(){
System.out.println("正在创建UseDao:" + this);
}
}
UserOracleDao.java
package com.imooc.spring.ioc.dao;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
@Repository
@Primary
public class UserOracleDao implements IUserDao{
public UserOracleDao(){
System.out.println("正在创建UserOracleDao:" + this);
}
}
IUserDao.java [接口]
package com.imooc.spring.ioc.dao;
public interface IUserDao {
}
DepartmentService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.IUserDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class DepartmentService {
/**
* 1. @Resource设置name属性,则按name在IoC容器中将bean注入
* 2. @Resource未设置name属性
* 2.1 以属性名作为bean name在IoC容器中匹配bean,如有匹配则注入
* 2.2 按属性名未匹配,则按类型进行匹配,同 @Autowired 需要加入 @Primary解决类型冲突
* 使用建议: 在使用 @Resource对象时推荐设置name或保证属性名与bean名称一致
*/
/*方法2.2.1
@Resource(name = "userOracleDao")
private IUserDao udao;*/
//方法2.2.2
@Resource
private IUserDao userOracleDao;
public void joinDepartment(){
System.out.println(userOracleDao);
}
}
UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.IUserDao;
import com.imooc.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//@Autowired
//Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值
//不再执行set方法
private IUserDao udao;
public UserService() {
System.out.println("正在构建UserService" + this);
}
public IUserDao getUdao() {
return udao;
}
@Autowired
//如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入
public void setUdao(UserDao udao) {
System.out.println("setUdao: " + udao);
this.udao = udao;
}
}
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Order;
import com.imooc.spring.ioc.service.DepartmentService;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getUdao());
DepartmentService departmentService = context.getBean("departmentService", DepartmentService.class);
departmentService.joinDepartment();
}
}
元数据注解
注解 | 说明 |
---|---|
@Primary | 按类型装配时出现多个相同类型对象,拥有此注解对象优先被注入 |
@PostConstruct | 描述方法,相当于XML中init-method配置的注解版本 |
@PreDestory | 描述方法,相当于XML中destory-method配置的注解版本 |
@Scope | 设置对象的scope属性 //@Scope(“prototype”)设置多例 |
@Value | 为属性注入静态数据 |
applicationContext.xml 【新增第11行】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载指定路径下的properties文件-->
<context:property-placeholder location="classpath:config.properties"/>
<!--
在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository
@Service
@Controller
@Component
-->
<context:component-scan base-package="com.imooc"/>
</beans>
config.properties [这里属于动态注入噢~]
metaData=imooc.com
UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.IUserDao;
import com.imooc.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
@("prototype") //设置多例 与XML中bean scope完全相同
public class UserService {
@Value("${metaData}")
private String metaData; //对此数据的动态注入
//@Autowired
//Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值
//不再执行set方法
public UserService() {
System.out.println("正在构建UserService" + this);
}
@PostConstruct //XML中bean init-method完全相同
public void init(){
System.out.println("初始化UserService对象,metaData=" + metaData);
}
private IUserDao udao;
public IUserDao getUdao() {
return udao;
}
@Autowired
//如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入
public void setUdao(UserDao udao) {
System.out.println("setUdao: " + udao);
this.udao = udao;
}
}
SpringApplication.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Order;
import com.imooc.spring.ioc.service.DepartmentService;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getUdao());
}
}
基于Java Config配置IoC容器
- 完全摆脱XML的束缚, 使用独立Java类管理对象与依赖
- 注解配置相对分散, 利用Java Config可对配置集中管理
- 可以在编译时进行依赖检查, 不容易出错
Java Config核心注解【敏捷开发 小型项目】 而XML是大型团队项目
注解 | 说明 |
---|---|
@Configuration | 描述类, 说明当前类是Java Config配置类, 完全替代XML文件 |
@Bean | 描述方法, 方法返回对象将被IoC容器管理, beanId默认为方法名 |
@ImportResource | 描述类, 加载静态文件, 可使用@Value注解获取 |
@ComponentScan | 描述类, 同XML的 < context:compoment-scan >标签 |
UserController.java
package com.imooc.spring.ioc.controller;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
UserDao.java
package com.imooc.spring.ioc.dao;
public class UserDao {
}
UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
public class UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Config.java [作为配置文件]
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.controller.UserController;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //当前是个配置类,用于代替applicationContext.xml
public class Config {
@Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
public UserDao userDao(){
UserDao userDao = new UserDao();
return userDao;
}
@Bean
public UserService userService(){
UserService userService = new UserService();
return userService;
}
@Bean
public UserController userController(){
UserController userController = new UserController();
return userController;
}
}
SpringApplication.java
package com.imooc.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
//基于Java Config配置IoC容器的初始化
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
String[] ids = context.getBeanDefinitionNames();
for (String id:ids){
System.out.println(id+":"+context.getBean(id));
}
}
}
=======================================================
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@4f9a3314
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@3b2c72c2
org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@491666ad
org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@176d53b2
org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@971d0d8
config:com.imooc.Config.Config$$EnhancerBySpringCGLIB$$c932e406@51931956
userDao:com.imooc.Config.UserDao@2b4a2ec7
userService:com.imooc.Config.UserService@564718df
userController:com.imooc.Config.UserController@51b7e5df
JavaConfig-对象依赖注入
Config的括号增加参数
Config.java
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.controller.UserController;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //当前是个配置类,用于代替applicationContext.xml
@ComponentScan(basePackages = "com.imooc") //扫描其他的组件类 因为其他人也会写
public class Config {
@Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
public UserDao userDao(){
UserDao userDao = new UserDao();
System.out.println("已创建"+userDao);
return userDao;
}
@Bean
//先按name尝试注入,name不存在则按类型注入 冲突则@Primary
public UserService userService(UserDao userDao){
UserService userService = new UserService();
System.out.println("已创建"+userService);
userService.setUserDao(userDao);
System.out.println("调用setUserDao:" + userDao);
return userService;
}
@Bean //<bean id="xxx" class="xxx">
public UserController userController(UserService userService){
UserController userController = new UserController();
System.out.println("已创建" + userController);
userController.setUserService(userService);
System.out.println("调用setUserService:"+userService);
return userController;
}
}
SpringApplication.java
package com.imooc.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
//基于Java Config配置IoC容器的初始化
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println("=============================");
String[] ids = context.getBeanDefinitionNames();
for (String id:ids){
System.out.println(id+":"+context.getBean(id));
}
}
}
=================================================================
已创建com.imooc.spring.ioc.dao.UserDao@10b48321
已创建com.imooc.spring.ioc.service.UserService@473b46c3
调用setUserDao:com.imooc.spring.ioc.dao.UserDao@10b48321
已创建com.imooc.spring.ioc.controller.UserController@797badd3
调用setUserService:com.imooc.spring.ioc.service.UserService@473b46c3
=============================
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@44a664f2
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@7f9fcf7f
org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@2357d90a
org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@6328d34a
org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@145eaa29
config:com.imooc.spring.ioc.Config$$EnhancerBySpringCGLIB$$8a99aa4c@15bb6bea
userDao:com.imooc.spring.ioc.dao.UserDao@10b48321
userService:com.imooc.spring.ioc.service.UserService@473b46c3
userController:com.imooc.spring.ioc.controller.UserController@797badd3
Process finished with exit code 0
如果突然想增加一个注入employeeDao
UserService.java 先增加一个私有的名字 再getter+setter
去Conifg.java 括号里新增
public UserService userService(UserDao userDao, EmployeeDao employeeDao){
UserService userService = new UserService();
System.out.println("已创建"+userService);
userService.setUserDao(userDao);
System.out.println("调用setUserDao:" + userDao);
userService.setEmployeeDao(employeeDao);
return userService;
}
Spring Test测试模块
- Spring Test是Spring中用于测试的模块
- Spring Test对JUnit单元测试框架有良好的整合
- 通过Spring Test可在Junit单元测试时自动初始化IoC容器
Spring与JUnit4整合过程
- Maven工程依赖spring-test
- 利用 @RunWith 与 @ContextConfiguration描述测试用例类
- 测试用例类从容器获取对象完成测试用例的执行
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Spring_test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
UserDao.java
package com.imooc.spring.ioc.dao;
public class UserDao {
public void insert(){
System.out.println("新增用户数据");
}
}
UserService.java
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
public class UserService {
private UserDao userDao;
public void createUser(){
System.out.println("调用创建用户业务代码");
userDao.insert();
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userDao" class="com.imooc.spring.ioc.dao.UserDao">
</bean>
<bean id="userService" class="com.imooc.spring.ioc.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
SpringTestor.java
import com.imooc.spring.ioc.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
//将Junit4的执行权交给Spring Test,在测试用例执行前自动初始化IoC容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringTestor {
@Resource
private UserService userService;
@Test
public void testUserService(){
userService.createUser();
}
}
介绍AOP
Aspect Oriented Programming(AOP)从某种意义上说是对OOP的补充,因为它还提供了模块化的功能。但是模块化的关键单元是方面而不是类。
AOP面向切面编程
- 介绍Spring AOP与相关概念名词
- Spring AOP开发与配置流程
- Spring 五种通知类型与应用场景
Spring中的可插拔组件技术 [类似于插件]
应用程序执行前检测的作用
Spring AOP [在不修改源码的情况下对程序进行扩展]
[IDEA创建项目出现Cannot resolve plugin org.apache.maven.plugins:maven-clean-plugin:2.5-CSDN博客](https://blog.csdn.net/weixin_45429409/article/details/118068484?ops_request_misc=%7B%22request%5Fid%22%3A%22170678007016800227455895%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=170678007016800227455895&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-118068484-null-null.142^v99^pc_search_result_base9&utm_term=Cannot resolve plugin org.apache.maven.plugins%3Amaven-site-plugin%3A3.3&spm=1018.2226.3001.4187)
- Spring AOP - Aspect Oriented Programming 面向切面编程
- AOP的做法是将通用、与业务无关的功能抽象封装为切面类
- 切面可配置在目标方法的执行前、后运行,真正做到即插即用
运行前进行拦截在运行前打印时间 再运行代码;没有IoC就没有AOP;写完扩展之后要再applicationContext.xml中新增< aop:config >…来认可切面
aop/dao/EmployeeDao.java
package com.imooc.spring.aop.dao;
/**
* 员工表Dao
*/
public class EmployeeDao {
public void insert(){
System.out.println("新增员工数据");
}
}
---------------------------------------------
aop/dao/UserDao.java
package com.imooc.spring.aop.dao;
/**
* 用户表Dao
*/
public class UserDao {
public void insert(){
System.out.println("新增用户数据");
}
}
---------------------------------------------
aop/service/EmployeeService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.EmployeeDao;
import java.util.Date;
/**
* 员工服务
*/
public class EmployeeService {
private EmployeeDao employeeDao;
public void entry(){
System.out.println("执行员工入职业务逻辑");
employeeDao.insert();
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
---------------------------------------------
aop/service/UserService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.UserDao;
/**
* 用户服务
*/
public class UserService {
private UserDao userDao;
public void createUser(){
if(1==1){
throw new RuntimeException("用户已存在");
}
System.out.println("执行创建用户业务逻辑");
userDao.insert();
}
public String generateRandomPassword(String type , Integer length){
System.out.println("按" + type + "方式生成"+ length + "位随机密码");
return "Zxcquei1";
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
---------------------------------------------
aop/aspect/MethodAspect
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.JoinPoint;
import java.text.SimpleDateFormat;
import java.util.Date;
//切面类
public class MethodAspect {
//切面方法,用于扩展额外功能
//JoinPoint 连接点,通过连接点可以获取目标类/方法的信息
public void printExecutionTime(JoinPoint joinPoint){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
String className = joinPoint.getTarget().getClass().getName();//获取类→获取目标类的名称
String methodName = joinPoint.getSignature().getName();//获取目标方法名称
System.out.println("---->" + now + ":" + className + "." + methodName);
Object[] args = joinPoint.getArgs();
System.out.println("---->参数个数:" + args.length);
for(Object arg:args){
System.out.println("---->参数:" + arg);
}
}
public void doAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("<----返回后通知:" + ret);
}
public void doAfterThrowing(JoinPoint joinPoint,Throwable th){
System.out.println("<----异常通知:" + th.getMessage());
}
public void doAfter(JoinPoint joinPoint){
System.out.println("<----触发后置通知");
}
}
SpringApplication.java
package com.imooc.spring.aop;
import com.imooc.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser(); //模拟创建新用户过程
userService.generateRandomPassword("MD5", 16);
}
}
====================================================================
---->2024-02-01 18:09:38 050:com.imooc.AOP.UserService.createUser
---->参数个数:0
执行创建用户业务逻辑
新增用户数据
<----返回后通知:null
<----触发后置通知
---->2024-02-01 18:09:38 058:com.imooc.AOP.UserService.generateRandomPassword
---->参数个数:2
---->参数:MD5
---->参数:16
按MD5方式生成16位随机密码
<----返回后通知:PzZo3Fzqe!r4$
<----触发后置通知
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="com.imooc.spring.aop.dao.UserDao"/>
<bean id="employeeDao" class="com.imooc.spring.aop.dao.EmployeeDao"/>
<bean id="userService" class="com.imooc.spring.aop.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.aop.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
<!-- AOP配置 迎接新MethodAspect切面的到来-->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean>
<aop:config>
<!-- PointCut 切点,使用execution表达式描述切面的作用范围 -->
<!-- execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上 -->
<!--<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>-->
<!--只对所有Service类生效-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>
<!--只对所有返回值为String类型方法生效-->
<!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"></aop:pointcut>-->
<!--对方法名进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"></aop:pointcut>-->
<!-- 对参数进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(String,*))"></aop:pointcut>-->
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect"> <!--关联一下-->
<!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() -->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc.spring</groupId>
<artifactId>aop</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--aspectjweaver是Spring AOP的底层依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
</project>
Spring AOP 与 AspectJ的关系 [实现类与方法的匹配]
Spring【SpringAOP(通知类型、切点表达式 、多切面配置 、注解配置AOP、原生Spring实现AOP)】(六)-全面详解(学习总结—从入门到深化)_童小纯呀的技术博客_51CTO博客
- Eclips AspectJ 是一种基于Java平台的面向切面编程的语言
- Spring AOP使用AspectJWeaver实现类与方法匹配
- Spring AOP利用代理模式实现对象运行时功能扩展
几个关键概念
注解 | 说明 |
---|---|
Aspect | 切面,具体的可插拔组件功能类,通常一个切面只能实现一个通用功能 |
Target Class/Method | 目标类、目标方法,指真正要执行与业务相关的方法 |
PointCut | 切入点,使用execution表达式说明切面要作用再系统的哪些类上 |
JoinPoint | 连接点,切面运行过程中是包含了目标类/方法元数据的对象 |
Advice | 通知,说明具体的切面的执行时机,Spring包含了不中不同类型通知 |
JoinPoint 连接点,通过连接点可以获取目标类/方法的信息
public void printExecutionTime(JoinPoint joinPoint){}
Spring AspectJ AOP实现提供了许多注释:
@Aspect 将该类声明为方面。
@Pointcut 声明切入点表达式。
用于创建建议的注释如下:
@Before 声明before建议。在调用实际方法之前将其应用。
@After 声明after建议。在调用实际方法之后并返回结果之前应用。
@AfterReturning 声明返回建议之后。在调用实际方法之后并返回结果之前应用。但是您可以在建议中获得结果值。
@Around 声明环绕建议。它在调用实际方法之前和之后应用。
@AfterThrowing 声明了throws建议。如果实际方法引发异常,则应用此方法。
AOP配置过程
- 依赖AspectJ
- 实现切面类/方法
- 配置Aspect Bean
- 定义PointCut
- 配置Advice //before通知(Advice)
JoinPoint核心方法
注解 | 说明 |
---|---|
Object getTarget() | 获取IoC容器内目标对象 |
Signature getSignature() | 获取目标方法 |
Object[] getArgs() | 获取目标方法参数 |
//很多线上的项目需要跟踪调试却不知道输入的参数是什么 就可以增加切片
public void printExecutionTime(JoinPoint joinPoint){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
String className = joinPoint.getTarget().getClass().getName();//获取类→获取目标类的名称
String methodName = joinPoint.getSignature().getName();//获取目标方法名称
System.out.println("---->" + now + ":" + className + "." + methodName);
Object[] args = joinPoint.getArgs(); //传入目标方法的实际参数
System.out.println("---->参数个数:" + args.length);
for(Object arg:args){
System.out.println("---->参数:" + arg);
}
}
PointCut切点表达式 [@pointcut]
注释用于定义切入点。我们也可以通过名称引用切入点表达式
@Pointcut("execution(* Operation.*(..))")
private void doSomething() {}
//它将应用于所有公共方法
@Pointcut("execution(public * * (..))")
//它将应用于Operation类的所有公共方法
@Pointcut("execution(public Operation.*(..))")
//它将应用于Operation类的所有方法
@Pointcut("execution(* Operation.*(..))")
//它将应用于Employee类的所有公共设置方法
@Pointcut("execution(public Employee.set*(..))")
//它将应用于所有返回int值的Operation类方法
@Pointcut("execution(int Operation.*(..))")
public void com.imooc.service.UserService.createUser(形参1,形参2,..)
↑ ↑ ↑ ↑ ↑ ↑ ↑execution(public * com.imooc .. * . * ( .. ))
*通配符 ..包通配符[包括所有后代查找] (..)参数通配符第一项public可以默认
<!-- AOP配置 迎接新MethodAspect切面的到来-->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean>
<aop:config>
<!-- PointCut 切点,使用execution表达式描述切面的作用范围 -->
<!-- execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上 -->
<!--<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>-->
<!--只对所有Service类生效-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>
</aop:config>
---------------------------------------------------------------------------------
切点表达式训练
<!--只对所有返回值为String类型方法生效-->
<!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"></aop:pointcut>-->
<!--对方法名进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"></aop:pointcut>-->
<!-- 对参数进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(String,*))"></aop:pointcut>-->
<!-- 定义切面类 -->
五种通知类型
注解 | 说明 |
---|---|
Before Advice | 前置通知,目标方法运行前执行 |
After Returning Advice | 返回后通知,目标方法返回数值后执行 |
After Throwing Advice | 异常通知,目标方法抛出异常后执行 |
After Advice | 后置通知,目标方法运行后执行 |
Around Advice | 最强大通知,自定通知执行时机,可决定目标方法是否运行 |
@Around("execution(* com.imooc..*Service.*(..))") 【通知 切面表达式】
// @Before() @After() @AfterThrowing @AfterReturning
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
用于创建建议的注释如下:
@Before 声明before建议。在调用实际方法之前将其应用。
@After 声明after建议。在调用实际方法之后并返回结果之前应用。
@AfterReturning 声明返回建议之后。在调用实际方法之后并返回结果之前应用。但是您可以在建议中获得结果值。
@Around 声明环绕建议。它在调用实际方法之前和之后应用。
@AfterThrowing 声明了throws建议。如果实际方法引发异常,则应用此方法。
让我们看看用于定义建议的xml元素:
Spring AOP AspectJ Xml配置示例 - Spring教程 - 菜鸟教程 (cainiaoplus.com)
**aop: before
**在调用实际的业务逻辑方法之前应用。
**aop: after
**在调用实际的业务逻辑方法之后应用。
**aop: 返回后
**在调用实际的业务逻辑方法后应用。可用于拦截通知中的返回值。
**aop: around
**在调用实际的业务逻辑方法之前和之后都将应用。
**aop: 投掷后
**如果实际的业务逻辑方法抛出异常,则将其应用。
特殊的“通知” - 引介增强
- 引介增强是对类的增强,而非方法
- 引介增强允许再运行时为目标类增加新属性或方法
- 引介增强允许再运行时改变类的行为,让类随运行环境动态变更
MethodAspect.java
public void doAfter(JoinPoint joinPoint){
System.out.println("<----触发后置通知");
}
-----------------------------------------------
applicationContext.xml
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
MethodAspect.java
public void doAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("<----返回后通知:" + ret);
}
-----------------------------------------------
applicationContext.xml
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
MethodAspect.java
public void doAfterThrowing(JoinPoint joinPoint,Throwable th){
System.out.println("<----异常通知:" + th.getMessage());
}
-----------------------------------------------
applicationContext.xml
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
打印顺序根据applicationContext.xml代码排列顺序一致
applicationContext.xml [最终]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="com.imooc.spring.aop.dao.UserDao"/>
<bean id="employeeDao" class="com.imooc.spring.aop.dao.EmployeeDao"/>
<bean id="userService" class="com.imooc.spring.aop.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.aop.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
<!-- AOP配置 迎接新MethodAspect切面的到来-->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean>
<aop:config>
<!-- PointCut 切点,使用execution表达式描述切面的作用范围 -->
<!-- execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上 -->
<!--<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>-->
<!--只对所有Service类生效-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>
<!--只对所有返回值为String类型方法生效-->
<!--<aop:pointcut id="pointcut" expression="execution(String com.imooc..*Service.*(..))"></aop:pointcut>-->
<!--对方法名进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.create*(..))"></aop:pointcut>-->
<!-- 对参数进行约束 -->
<!--<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(String,*))"></aop:pointcut>-->
<!-- 定义切面类 -->
<aop:aspect ref="methodAspect"> <!--关联一下-->
<!-- before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime() -->
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
<aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
</beans>
SpringApplication.java
package com.imooc.spring.aop;
import com.imooc.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser(); //模拟创建新用户过程
userService.generateRandomPassword("MD5", 15);
}
}
===========================================================
---->2024-01-12 13:40:56 885:com.imooc.spring.aop.service.UserService.createUser
---->参数个数:0
<----异常通知:用户已存在
<----触发后置通知
Exception in thread "main" java.lang.RuntimeException: 用户已存在
@Before示例
在实际业务逻辑方法之前应用AspectJ Before Advice。您可以在此处执行任何操作,例如转换,身份验证等。
创建一个包含实际业务逻辑的类
Operation.java
package com.nhooo;
public class Operation{
public void msg(){System.out.println("msg method invoked");}
public int m(){System.out.println("m method invoked");return 2;}
public int k(){System.out.println("k method invoked");return 3;}
}
现在,创建包含在建议之前的方面类
TrackOperation.java
package com.nhooo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TrackOperation{
//它将应用于Operation类的所有方法。
@Pointcut("execution(* Operation.*(..))")
public void k(){}//pointcut name
@Before("k()")//在before通知上应用切入点
public void myadvice(JoinPoint jp)//it is advice (before advice)
{
System.out.println("additional concern");
//System.out.println("Method Signature: " + jp.getSignature());
}
}
现在创建定义bean的applicationContext.xml文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="opBean" class="com.nhooo.Operation"> </bean>
<bean id="trackMyBean" class="com.nhooo.TrackOperation"></bean>
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"></bean>
</beans>
现在,让我们称为实际方法
Test.java
package com.nhooo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Operation e = (Operation) context.getBean("opBean");
System.out.println("calling msg...");
e.msg();
System.out.println("calling m...");
e.m();
System.out.println("calling k...");
e.k();
}
}
===========================================================================
calling msg...
additional concern
msg() method invoked
calling m...
additional concern
m() method invoked
calling k...
additional concern
k() method invoked
apo:before示例
在实际业务逻辑方法之前应用”先行AspectJ建议”。您可以在此处执行任何操作,例如转换,身份验证等。
创建一个包含实际业务逻辑的类
Operation.java
package com.nhooo;
public class Operation{
public void msg(){System.out.println("msg method invoked");}
public int m(){System.out.println("m method invoked");return 2;}
public int k(){System.out.println("k method invoked");return 3;}
}
现在,创建包含在建议之前的方面类
TrackOperation.java
package com.nhooo;
import org.aspectj.lang.JoinPoint;
public class TrackOperation{
public void myadvice(JoinPoint jp)//it is advice
{
System.out.println("additional concern");
//System.out.println("Method Signature: " + jp.getSignature());
}
}
现在创建定义bean的applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy />
<bean id="opBean" class="com.nhooo.Operation"> </bean>
<bean id="trackAspect" class="com.nhooo.TrackOperation"></bean>
<aop:config>
<aop:aspect id="myaspect" ref="trackAspect" >
<!-- @Before -->
<aop:pointcut id="pointCutBefore" expression="execution(* com.nhooo.Operation.*(..))" />
<aop:before method="myadvice" pointcut-ref="pointCutBefore" />
</aop:aspect>
</aop:config>
</beans>
现在,让我们称为实际方法
Test.java
package com.nhooo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Operation e = (Operation) context.getBean("opBean");
System.out.println("calling msg...");
e.msg();
System.out.println("calling m...");
e.m();
System.out.println("calling k...");
e.k();
}
}
===================================================================
calling msg...
additional concern
msg() method invoked
calling m...
additional concern
m() method invoked
calling k...
additional concern
k() method invoked
如您所见,在调用msg(),m()和k()方法之前,还会打印出其他问题。
@After示例
在调用实际的业务逻辑方法之后,应用了after建议之后的AspectJ。它可以用来维护日志,安全性,通知等。
在这里,我们假设 Operation.java , applicationContext.xml 和 Test.java 文件与@Before示例中给出的文件相同。
TrackOperation.Java
package com.nhooo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TrackOperation{
@Pointcut("execution(* Operation.*(..))")
public void k(){}//pointcut name
@After("k()")//applying pointcut on after advice
public void myadvice(JoinPoint jp)//it is advice (after advice)
{
System.out.println("additional concern");
//System.out.println("Method Signature: " + jp.getSignature());
}
}
===============================================================
calling msg...
msg() method invoked
additional concern
calling m...
m() method invoked
additional concern
calling k...
k() method invoked
additional concern
aop:after示例
在调用实际的业务逻辑方法之后,应用了通知之后的AspectJ。它可用于维护日志,安全性,通知等。
在这里,我们假设 **Operation.java
**, **TrackOperation.java
**和 **Test.java
**文件与aop:before 中的示例相同。
现在创建定义bean的applicationContext.xml文件。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy />
<bean id="opBean" class="com.nhooo.Operation"> </bean>
<bean id="trackAspect" class="com.nhooo.TrackOperation"></bean>
<aop:config>
<aop:aspect id="myaspect" ref="trackAspect" >
<!-- @After -->
<aop:pointcut id="pointCutAfter" expression="execution(* com.nhooo.Operation.*(..))" />
<aop:after method="myadvice" pointcut-ref="pointCutAfter" />
</aop:aspect>
</aop:config>
</beans>
calling msg... msg() method invoked additional concern calling m... m() method invoked additional concern calling k... k() method invoked additional concern
您可以看到在调用msg(),m()和k()方法之后,还会出现其他问题。
@AfterReturning示例
通过在返回建议后使用,我们可以在建议中获得结果。创建包含以下内容的类业务逻辑。
Operation.java
package com.nhooo;
public class Operation{
public int m(){System.out.println("m() method invoked");return 2;}
public int k(){System.out.println("k() method invoked");return 3;}
}
创建返回建议后包含的方面类
TrackOperation.java
package com.nhooo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TrackOperation{
@AfterReturning(pointcut = "execution(* Operation.*(..))", returning= "result")
public void myadvice(JoinPoint jp,Object result) //it is advice (after returning advice)
{
System.out.println("additional concern");
System.out.println("Method Signature: " + jp.getSignature());
System.out.println("Result in advice: "+result);
System.out.println("end of after returning advice...");
}
}
文件: applicationContext.xml 与@Before建议示例中给出的
文件: Test.java 现在创建调用实际方法的Test类。
Test.java
package com.nhooo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test{
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Operation e = (Operation) context.getBean("opBean");
System.out.println("calling m...");
System.out.println(e.m());
System.out.println("calling k...");
System.out.println(e.k());
}
}
================================================================
calling m...
m() method invoked
additional concern
Method Signature: int com.nhooo.Operation.m()
Result in advice: 2
end of after returning advice...
2
calling k...
k() method invoked
additional concern
Method Signature: int com.nhooo.Operation.k()
Result in advice: 3
end of after returning advice...
3
环绕通知 [public Object check(ProceedingJoinPoint pjp)]
利用AOP进行方法性能筛查 [筛查哪个实现时间长 可以进行代码优化]
dao/UserDao.java
package com.imooc.spring.aop.dao;
/**
* 用户表Dao
*/
public class UserDao {
public void insert(){
System.out.println("新增用户数据");
}
}
---------------------------------
dao/EmployeeDao.java
package com.imooc.spring.aop.dao;
/**
* 员工表Dao
*/
public class EmployeeDao {
public void insert(){
System.out.println("新增员工数据");
}
}
service/EmployeeService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.EmployeeDao;
/**
* 员工服务
*/
public class EmployeeService {
private EmployeeDao employeeDao;
public void entry(){
System.out.println("执行员工入职业务逻辑");
employeeDao.insert();
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
---------------------------------
service/UserService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.UserDao;
/**
* 用户服务
*/
public class UserService {
private UserDao userDao;
public void createUser(){
try {
Thread.sleep(3000); //沉睡 为了触发环绕通知
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行员工入职业务逻辑");
userDao.insert();
}
public String generateRandomPassword(String type , Integer length){
System.out.println("按" + type + "方式生成"+ length + "位随机密码");
return "Zxcquei1";
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
aspect/MethodChecker.java
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MethodChecker {
////ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();//方法执行前
Object ret = pjp.proceed();//执行目标方法
long endTime = new Date().getTime();//
long duration = endTime - startTime; //方法执行后 执行时长
if(duration >= 1000){//认为执行太慢了 打印信息
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;//方法返回
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="com.imooc.spring.aop.dao.UserDao"/>
<bean id="employeeDao" class="com.imooc.spring.aop.dao.EmployeeDao"/>
<bean id="userService" class="com.imooc.spring.aop.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.aop.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
<bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"></bean>
<aop:config> <!--所有类进行拦截-->
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"></aop:pointcut>
<aop:aspect ref="methodChecker">
<!--环绕通知-->
<aop:around method="check" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
SpringApplication.java
package com.imooc.spring.aop;
import com.imooc.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser();
}
}
==============================================================
执行员工入职业务逻辑
新增用户数据
=======2024-01-12 13:52:41 475:com.imooc.spring.aop.service.UserService.createUser(3010ms)======
Process finished with exit code 0
@Around示例
围绕通知的AspectJ在调用实际的业务逻辑方法之前和之后都得到应用。在这里,我们是假设 applicationContext.xml 文件与@Before示例中给出的文件相同。创建一个包含实际业务逻辑的类。
Operation.java
package com.nhooo;
public class Operation{
public void msg(){System.out.println("msg() is invoked");}
public void display(){System.out.println("display() is invoked");}
}
创建包含围绕建议的方面类。您需要在advice方法中传递 PreceedingJoinPoint 引用,以便我们可以通过调用proce来进行请求()方法。文件: TrackOperation.java
TrackOperation.java
package com.nhooo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TrackOperation
{
@Pointcut("execution(* Operation.*(..))")
public void abcPointcut(){}
@Around("abcPointcut()")
public Object myadvice(ProceedingJoinPoint pjp) throws Throwable
{
System.out.println("Additional Concern Before calling actual method");
Object obj=pjp.proceed();
System.out.println("Additional Concern After calling actual method");
return obj;
}
}
现在创建调用实际方法的Test类。
Test.java
package com.nhooo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test{
public static void main(String[] args){
ApplicationContext context = new classPathXmlApplicationContext("applicationContext.xml");
Operation op = (Operation) context.getBean("opBean");
op.msg();
op.display();
}
}
================================================================
Additional Concern Before calling actual method
msg() is invoked
Additional Concern After calling actual method
Additional Concern Before calling actual method
display() is invoked
Additional Concern After calling actual method
基于注解配置Spring AOP
aspect/MethodChecher.java
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component //标记当前类为组件
@Aspect //说明当前类是切面类
public class MethodChecker {
//环绕通知,参数为PointCut切点表达式
@Around("execution(* com.imooc..*Service.*(..))")
// @Before() @After() @AfterThrowing @AfterReturning
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = pjp.proceed();//执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime; //执行时长
if(duration >= 1000){
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
dao/UserDao
package com.imooc.spring.aop.dao;
import org.springframework.stereotype.Repository;
/**
* 用户表Dao
*/
@Repository
public class UserDao {
public void insert(){
System.out.println("新增用户数据");
}
}
----------------------------------------------
dao/EmployeeDao.java
package com.imooc.spring.aop.dao;
import org.springframework.stereotype.Repository;
/**
* 员工表Dao
*/
@Repository
public class EmployeeDao {
public void insert(){
System.out.println("新增员工数据");
}
}
service/UserService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.UserDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 用户服务
*/
@Service
public class UserService {
@Resource
private UserDao userDao;
public void createUser(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行员工入职业务逻辑");
userDao.insert();
}
public String generateRandomPassword(String type , Integer length){
System.out.println("按" + type + "方式生成"+ length + "位随机密码");
return "Zxcquei1";
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
---------------------------------------------------------
service/EmployeeService.java
package com.imooc.spring.aop.service;
import com.imooc.spring.aop.dao.EmployeeDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 员工服务
*/
@Service
public class EmployeeService {
@Resource
private EmployeeDao employeeDao;
public void entry(){
System.out.println("执行员工入职业务逻辑");
employeeDao.insert();
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
SpringApplication.java
package com.imooc.spring.aop;
import com.imooc.spring.aop.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.createUser();
}
}
========================================================================
执行员工入职业务逻辑
新增用户数据
=======2024-01-12 17:06:18 669:com.imooc.spring.aop.service.UserService.createUser(3015ms)======
Process finished with exit code 0
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--初始化IoC容器-->
<context:component-scan base-package="com.imooc"/>
★ <!--启用Spring AOP注解模式--> ★
<aop:aspectj-autoproxy/>
</beans>
增加了是说明这个类是需要被IoC实例化的
- 四种组件类型注解-声明当前类的功能与职责 【回忆上面的知识点】
注解 | 说明 |
---|---|
@Component | 组件注解,通用注解,被该注解描述的类将被IoC容器管理并实例化 |
@Controller | 语义注解,说明当前类是MVC应用中的控制器类 |
@Service | 语义注解,说明当前类是Service业务服务类 |
@Repository | 语义注解,说明当前类用于业务持久层,通常描述对应Dao类 |
Spring AOP实现原理
★★★★★ 面试 Spring AOP 底层的实现原理 ★★★★★★
- Spring基于代理模式实现功能动态扩展,包含两种形式:
- 目标类拥有接口,通过JDK动态代理实现功能扩展 来实现目标类的代理从而实现扩展
- 目标类没有接口,通过CGLib组件实现功能扩展 通过继承的方式实现扩展
代理模式
- 代理模式通过代理对象对原对象的实现功能扩展
基于UserService接口实现代理类 同时持有与之对应的具体实现
静态代理 [每一个具体的委托类都要手动创造一个代理类]
aop/service/UserService [接口]
package com.imooc.spring.aop.service;
public interface UserService {
public void createUser();
}
-------------------------------------
aop/service/UserServiceImpl.java
package com.imooc.spring.aop.service;
public class UserServiceImpl implements UserService{
public void createUser() {
System.out.println("执行创建用户业务逻辑");
}
}
-------------------------------------
aop/service/UserServiceProxy.java [代理]
package com.imooc.spring.aop.service;
import java.text.SimpleDateFormat;
import java.util.Date;
//静态代理是指必须手动创建代理类的代理模式使用方式
public class UserServiceProxy implements UserService{
//持有委托类的对象
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
public void createUser() {
System.out.println("=====" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"=========");
userService.createUser();
}
}
-------------------------------------
aop/service/UserServiceProxy1.java [代理1]
package com.imooc.spring.aop.service;
public class UserServiceProxy1 implements UserService{
private UserService userService ;
public UserServiceProxy1(UserService userService){
this.userService = userService;
}
public void createUser() {
userService.createUser();
System.out.println("========后置扩展功能======");
}
}
package com.imooc.spring.aop;
import com.imooc.spring.aop.service.UserService;
import com.imooc.spring.aop.service.UserServiceImpl;
import com.imooc.spring.aop.service.UserServiceProxy;
import com.imooc.spring.aop.service.UserServiceProxy1;
public class Application {
public static void main(String[] args) { //类似于二房东
UserService userService = new UserServiceProxy1(new UserServiceProxy(new UserServiceImpl()));
userService.createUser();
}
}
AOP底层逻辑—JDK动态代理
aop/service.UserService.java [接口]
package com.imooc.spring.aop.service;
public interface UserService {
public void createUser();
}
-------------------------------------
aop/service.EmployeeService.java [接口]
package com.imooc.spring.aop.service;
public interface EmployeeService {
public void createEmployee();
}
aop/service.EmployeeServiceImpl.java
package com.imooc.spring.aop.service;
public class EmployeeServiceImpl implements EmployeeService {
public void createEmployee() {
System.out.println("执行创建员工业务逻辑");
}
}
--------------------------------------
aop/service.UserServiceImpl.java
package com.imooc.spring.aop.service;
public class UserServiceImpl implements UserService{
public void createUser() {
System.out.println("执行创建用户业务逻辑");
}
}
ProxyInvocationHandler.java
package com.imooc.spring.aop.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
* InvocationHandler实现类与切面类的环绕通知类似
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;//目标对象
private ProxyInvocationHandler(Object target){
this.target = target;
}
/**
* 在invoke()方法对目标方法进行增强 反射method方法的时候遇见过invoke
* @param proxy 代理类对象
* @param method 目标方法对象
* @param args 目标方法实参
* @return 目标方法运行后返回值
* @throws Throwable 目标方法抛出的异常
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=====" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"=========");
Object ret = method.invoke(target, args);//调用目标方法,ProceedingJoinPoint.proceed()
return ret;
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
//动态创建代理类
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
invocationHandler);
userServiceProxy.createUser();
//动态代理,必须实现接口才可以运行
EmployeeService employeeService = new EmployeeServiceImpl();
EmployeeService employeeServiceProxy = (EmployeeService)Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),
employeeService.getClass().getInterfaces(),
new ProxyInvocationHandler(employeeService));
employeeServiceProxy.createEmployee();
}
}
=======================================================
=====2024-01-12 19:52:34 179=========
执行创建用户业务逻辑
=====2024-01-12 19:52:34 181=========
执行创建员工业务逻辑
Process finished with exit code 0

AOP底层逻辑—CGLib实现代理类
- CGLib是运行时字节码增强技术
- Spring AOP扩展无接口类使用CGLib
- AOP会运行时生成目标继承类字节码的方式进行行为扩展

Spring JDBC与事务管理
Spring JDBC
- Spring JDBC是Spring框架用于处理关系型数据库的模块
- Spring JDBC对JDBC API进行封装,极大简化开发工作量
- JdbcTemplate是Spring JDBC核心类, 提供数据CRUD方法
为什么有了Mybatis还需要Spring JDBC?
因为这两者面向的对象是不一样的
mybatis 封装程度高适合中小企业敏捷开发, 快速完成与数据库交互的工作, 封装程度高 执行效率低【慢】
Spring JDBC只是对原始的JDBC的API进行了简单封装 大厂使用【轻量级】可二次封装
Spring JDBC的使用步骤
Maven工程引入依赖
spring-jdbc
applicationContext.xml配置
DataSource
数据源 [用于指向连接哪数据库的哪种服务器 账号密码都是啥]在Dao注入
JdbcTemplate
对象, 实现数据CRUD
JdbcTemplate类
Spring JdbcTemplate类的方法
方法 | 说明 |
---|---|
public int update(String query) | 用于插入,更新和删除记录。 |
public int update(String query,Object … args) | 用于通过使用给定参数的PreparedStatement插入,更新和删除记录。 |
public void execute(String query) | 用于执行DDL查询。 |
public T execute(String sql, PreparedStatementCallback action) | 通过使用PreparedStatement回调执行查询。 |
public T query(String sql, ResultSetExtractor rse) | 用于使用ResultSetExtractor获取记录。 |
public List query(String sql, RowMapper rse) | 用于使用RowMapper获取记录。 |
JdbcTemplate实现增删改查
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc.spring</groupId>
<artifactId>jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
</project>
applicationContext.xml [SpringIoC基础配置文件]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 数据源设置:使用哪种服务器 哪种数据源 账号密码是什么 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 创建数据库连接 找数据源 JdbcTemplate提供数据CRUD的API 让IoC容器初始化的时候自动实例化jdbcTemplate 实例化的过程中要将设置好的数据源dataSource注入到jdbcTemplate属性中 此时就完成了实例化-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
<!-- 为Dao注入JdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
com/imooc/spring/jdbc/entity/Employee.java
public class Employee {
private Integer eno;
private String ename;
private Float salary;
private String dname;
private Date hiredate;
@Override
public String toString() {
return "Employee{" +
"eno=" + eno +
", ename='" + ename + '\'' +
", salary=" + salary +
", dname='" + dname + '\'' +
", hiredate=" + hiredate +
'}';
}
} +Setter Getter
com/imooc/spring/jdbc/dao/EmployeeDao.java
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class EmployeeDao {
/* 依赖与jdbcTemplate 要持有其
* <!-- 创建数据库连接 找数据源 JdbcTemplate提供数据CRUD的API-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
* */
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
// //将唯一转换的数据返回对应的对象 jdbcTemplate为了运行时的动态注入
String sql = "select * from employee where eno=?";
//将bean属性与每一行的一一对应 数据库记录到实体对象的转换
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
SpringApplication.java
package com.imooc.spring.jdbc;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
EmployeeDao employeeDao = context.getBean("employeeDao", EmployeeDao.class);
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
}
=======================================================
Employee{eno=3308, ename='张三', salary=6000.0, dname='研发部', hiredate=2011-05-08 00:00:00.0}
Process finished with exit code 0
JdbcTemplate的数据查询方法
com/imooc/spring/jdbc/dao/EmployeeDao.java
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class EmployeeDao {
/* 依赖与jdbcTemplate 要持有其
* <!-- 创建数据库连接 找数据源 JdbcTemplate提供数据CRUD的API-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
* */
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
// //将唯一转换的数据返回对应的对象 jdbcTemplate为了运行时的动态注入
String sql = "select * from employee where eno=?";
//将bean属性与每一行的一一对应 数据库记录到实体对象的转换
//查询单条数据 转换成对象
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql = "select * from employee where dname = ?";
//查询复合数据 sql 对应的参数数组 转换的Mapper对象
//多条记录 每一条记录转换为一个实体对象 放入List集合中进行返回
List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
JdbcTemplateTestor.java
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private EmployeeDao employeeDao;
@Test
public void testFindById(){
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
@Test
public void testFindByDname(){
System.out.println(employeeDao.findByDname("yan'fa'bu"));
}
}
================================================================
[Employee{eno=3308, ename='张三', salary=6000.0, dname='研发部', hiredate=2011-05-08 00:00:00.0}, Employee{eno=3420, ename='李四', salary=8700.0, dname='研发部', hiredate=2006-11-11 00:00:00.0}]
Process finished with exit code 0
★★ 新增 ★★
在没有对应的实体类情况下也可以得到相应的结果 结果被封装成为了Map对象
EmployeeDao.java
// 按列表返回每一条按Map对象列表返回
public List findMapByDname(String dname){
String sql = "select eno as empno, salary as a from employee where dname = ?";
//将查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
JdbcTemplateTestor.java
@Test
public void testFindMapByDname(){
System.out.println(employeeDao.findMapByDname("研发部"));
}
================================================================
[{empno=3308, a=6000.0}, {empno=3420, a=8700.0}]
Process finished with exit code 0
总结 [★★★★ queryForObject query queryForList ★★★★]
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
// //将唯一转换的数据返回对应的对象 jdbcTemplate为了运行时的动态注入
String sql = "select * from employee where eno=?";
//将bean属性与每一行的一一对应 数据库记录到实体对象的转换
// ★★查询单条数据 转换成对象 ★★
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql = "select * from employee where dname = ?";
// ★★查询复合数据★★ sql 对应的参数数组 转换的Mapper对象
// ★★多条记录 每一条记录转换为一个实体对象★★ 放入List集合中进行返回
List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
// 按列表返回每一条按Map对象列表返回
//特殊情况:无法进行实体类的映射则可用直接用queryForList特殊返回Map 来完成对应数据的封装
public List findMapByDname(String dname){
String sql = "select eno as empno, salary as a from employee where dname = ?";
//将查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
JdbcTemplate实现增删改查
★★ 增 ★★
EmployeeDao.java
public void insert(Employee employee){
String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
jdbcTemplate.update(sql,new Object[]{
employee.getEno(), employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate()
});
}
JdbcTemplateTestor.java
@Test
public void testinsert(){
Employee employee = new Employee();
employee.setEno(8888);
employee.setEname("赵六");
employee.setSalary(6666f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
★★ 更新 ★★
EmployeeDao.java
public int update(Employee employee){
String sql = "UPDATE employee SET ename=?, salary=?, dname=?, hiredate=? WHERE eno=?";
int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
return count;
}
JdbcTemplateTestor.java
@Test
public void testUpdate() {
Employee employee = employeeDao.findById(8888);
employee.setSalary(employee.getSalary() + 1000);
int count = employeeDao.update(employee);
System.out.println("本次更新" + count + "条数据");
}
=========================================================
本次更新1条数据
Process finished with exit code 0
★★ 删除 ★★
EmployeeDao.java
public int delete(Integer eno){
String sql = "delete from employee where eno=?";
int update = jdbcTemplate.update(sql, new Object[]{eno});
return update;
}
JdbcTemplateTestor.java
@Test
public void testDelete() {
int count = employeeDao.delete(8888);
System.out.println("本次删除" + count + "条数据");
}
=========================================================
本次删除1条数据
事务
- 事务式以一种可靠、一致的方法,访问和操作数据库的程序单元
- 说人话:要么把事情做完,要么什么都不做,不要做一半
- 事务依赖于数据库实现,MySQL通过事务区作为数据缓冲地带
编程式事务 [手动控制啥时候提交 啥时候回滚]
- 编程式事务是指通过代码手动提交回滚事务的事务控制方法
- SpringJDBC通过TransactionManager事务管理器实现事务控制
- 事务管理器提供commit/rollback方法进行事务提交与回滚
S1需求:公司来了10名新员工 批量导入员工表中 (要么什么都不做,要么把事情做完) 【类似银行存钱】
com/imooc/spring/jdbc/service/EmployeeService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import java.util.Date;
public class EmployeeService {
private EmployeeDao employeeDao;
public void bathImport(){
for (int i = 0; i < 10; i++) {
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
applicationContext.xml
<bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
JdbcTemplateTestor.java
@Resource
private EmployeeService employeeService;
@Test
public void testBatchImport(){
employeeService.bathImport();
System.out.println("批量导入成功");
}
此方法是一条一条的增加写入sql操作
09:47:33.565 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
09:47:33.565 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)]
09:47:33.567 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
09:47:33.567 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true]
09:47:33.684 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQLWarning ignored: SQL state '22007', error code '1292', message [Incorrect date value: '2024-01-14 09:47:33.564' for column 'hiredate' at row 1]
09:47:33.690 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
09:47:33.690 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)]
09:47:33.690 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
09:47:33.690 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true]
09:47:33.695 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQLWarning ignored: SQL state '22007', error code '1292', message [Incorrect date value: '2024-01-14 09:47:33.69' for column 'hiredate' at row 1]
09:47:33.695 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
编程式事务-2 [放入事务区统一管理]
com/imooc/spring/jdbc/service/EmployeeService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.Date;
public class EmployeeService {
private EmployeeDao employeeDao;
private DataSourceTransactionManager transactionManager;
public void bathImport(){
//定义了事务默认的标准配置
TransactionDefinition definition = new DefaultTransactionDefinition();
//开始一个事务 放在事务区中统一进行管理
TransactionStatus status = transactionManager.getTransaction(definition);
try {
for (int i = 0; i < 10; i++) {
if (i==3){
throw new RuntimeException("意料之外的异常");
}
Employee employee = new Employee();
employee.setEno(8010 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
//提交事务
transactionManager.commit(status);
} catch (Exception e) {
//回滚事务
transactionManager.rollback(status);
e.printStackTrace();
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public DataSourceTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(DataSourceTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 数据源设置:使用哪种服务器 哪种数据源 账号密码是什么 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 创建数据库连接 找数据源 JdbcTemplate提供数据CRUD的API-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
<!-- 为Dao注入JdbcTemplate对象 -->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<!-- 将属性进行注入 要添加其getter setter-->
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!-- 事务管理器 数据源 [整体提交以及回滚] 需要控制的类中注入bean id="transactionManager" -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
JdbcTemplateTestor.java
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import com.imooc.spring.jdbc.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private EmployeeDao employeeDao;
@Resource
private EmployeeService employeeService;
@Test
public void testFindById() {
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
@Test
public void testFindByDname() {
System.out.println(employeeDao.findByDname("市场部"));
}
@Test
public void testFindMapByDname() {
System.out.println(employeeDao.findMapByDname("研发部"));
}
@Test
public void testinsert() {
Employee employee = new Employee();
employee.setEno(8888);
employee.setEname("赵六");
employee.setSalary(6666f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
@Test
public void testUpdate() {
Employee employee = employeeDao.findById(8888);
employee.setSalary(employee.getSalary() + 1000);
int count = employeeDao.update(employee);
System.out.println("本次更新" + count + "条数据");
}
@Test
public void testDelete() {
int count = employeeDao.delete(8888);
System.out.println("本次删除" + count + "条数据");
}
@Test
public void testBatchImport(){
employeeService.bathImport();
System.out.println("批量导入成功");
}
}
========================================================
都是在一个数据库连接里操作的
10:01:16.165 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback
10:01:16.165 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@c88a337]
10:01:16.166 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@c88a337] after transaction
java.lang.RuntimeException: 意料之外的异常
at com.imooc.spring.jdbc.service.EmployeeService.bathImport(EmployeeService.java:24)
at JdbcTemplateTestor.testBatchImport(JdbcTemplateTestor.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
★★★ 编程式事务 ★★★
优点:容易理解 编程快
缺点:容易受到人为因素影响 结果致命
声明式事务
- 声明式事务指在不修改源代码情况下通过配置形式自动实现事务控制, 声明式事务本质就是AOP环绕通知
- 当目标方法执行成功时, 自动提交事务
- 当目标方法抛出运行异常时, 自动事务回滚
配置过程
- 配置TransactionManager事务管理器
- 配置事务通知与事务属性
- 为事务通知绑定PointCut切点
需求:在不修改原始代码的情况下 配置Service里面的声明式事务
pom.xml
<!--logback日志组件,Spring框架默认集成-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
applicationContext.xml
<!-- 1-3具体含义:当我们目标方法是imooc包下Service 且方法名为batchImport的时候 则认为当前方法需要使用事务-->
<!-- 利用transactionManager来完成对事务的提交以及回滚-->
<!-- 1.事务管理器,用于创建事务/提交/回滚 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务 advice是通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- ★★★★ 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 propagation="REQUIRED"需要使用 propagation是事务传播行为★★★★-->
<tx:method name="batchImport" propagation="REQUIRED"/>
<tx:method name="batch*" propagation="REQUIRED"/>
<!-- 设置所有findXXX方法不需要使用事务 不支持事务-->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3. 定义声明式事务的作用范围-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
EmployeeService.java
vpackage com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import java.util.Date;
public class EmployeeService {
private EmployeeDao employeeDao;
private BatchService batchService;
public void batchImport() {
for (int i = 1; i <= 10; i++) {
//注意噢 这里是被注释的
// if(i==3){
// throw new RuntimeException("意料之外的异常");
// }
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public BatchService getBatchService() {
return batchService;
}
public void setBatchService(BatchService batchService) {
this.batchService = batchService;
}
}
EmployeeDao.java 未改变
JdbcTemplateTestor.java
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import com.imooc.spring.jdbc.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private EmployeeDao employeeDao;
@Resource
private EmployeeService employeeService;
@Test
public void testFindById(){
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
@Test
public void testFindByDname(){
System.out.println(employeeDao.findByDname("市场部"));
}
@Test
public void testFindMapByDname(){
System.out.println(employeeDao.findMapByDname("研发部"));
}
@Test
public void testInsert(){
Employee employee = new Employee();
employee.setEno(8888);
employee.setEname("赵六");
employee.setSalary(6666f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
@Test
public void testUpdate(){
Employee employee = employeeDao.findById(8888);
employee.setSalary(employee.getSalary() + 1000);
int count = employeeDao.update(employee);
System.out.println("本次更新" + count + "条数据");
}
@Test
public void testDelete(){
int count = employeeDao.delete(8888);
System.out.println("本次删除" + count + "条数据");
}
@Test
public void testBatchImport(){
employeeService.batchImport();
System.out.println("批量导入成功");
}
@Test
public void testStartImportJob(){
employeeService.startImportJob();
}
}
事务传播行为【面试】
- 事务传播行为是指多个拥有事务的方法在嵌套调用时的事务控制方式
XML:<tx:method name="..." propagation="REQUIRED"/>
- 注解:
@Transactional(propagation=Propagation.REQUIRED)
加个BatchService 然后在EmployeeService加一个private BatchService batchService;(getter + setter) 再次插入一个public void startImportJob()
进行applicationContext.xml的bean注入
会被声明式事务范围包裹噢
<aop:pointcut id=”pointcut” expression=”execution(* com.imooc..*Service.*(..))”/>applicationContext.xml 加入 importJob1 importJob2 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 propagation="REQUIRED"需要使用--> <tx:method name="batchImport" propagation="REQUIRED"/> <tx:method name="batch*" propagation="REQUIRED"/> <!-- 设置所有findXXX方法不需要使用事务 --> <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/> <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/> <tx:method name="importJob1" propagation="REQUIRES_NEW"/> <tx:method name="importJob2" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
补充public void startImportJob()
public void startImportJob(){ //会有关联行为 若2失败则无法导入 1也会回滚 batchService.importJob1(); if(1==1){ throw new RuntimeException("意料之外的异常"); } batchService.importJob2(); System.out.println("批量导入成功"); } //方法: REQUIRES_NEW 不同方法运行在不同事务中 //事务1开始 事务1挂起事务2开始 事务2提交 事务1恢复 事务1挂起事务3开始 事务3提交事务1恢复 事务1提交 <tx:method name="importJob1" propagation="REQUIRES_NEW"/> <tx:method name="importJob2" propagation="REQUIRES_NEW"/> 上面的分这写
com/imooc/spring/jdbc/service/BatchService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import java.util.Date;
public class BatchService {
private EmployeeDao employeeDao;
public void importJob1(){
for (int i = 1; i <= 10; i++) {
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("研发部员工" + i);
employee.setSalary(4000f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public void importJob2(){
for (int i = 1; i <= 10; i++) {
Employee employee = new Employee();
employee.setEno(9000 + i);
employee.setEname("市场部员工" + i);
employee.setSalary(4500f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
com/imooc/spring/jdbc/service/EmployeeService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import java.util.Date;
public class EmployeeService {
private EmployeeDao employeeDao;
private BatchService batchService;
public void batchImport() {
for (int i = 1; i <= 10; i++) {
// if(i==3){
// throw new RuntimeException("意料之外的异常");
// }
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public void startImportJob(){
batchService.importJob1();
if(1==1){
throw new RuntimeException("意料之外的异常");
}
batchService.importJob2();
System.out.println("批量导入成功");
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public BatchService getBatchService() {
return batchService;
}
public void setBatchService(BatchService batchService) {
this.batchService = batchService;
}
}
com/imooc/spring/jdbc/dao/EmployeeDao.java
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
public class EmployeeDao {
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
String sql = "select * from employee where eno = ?";
//查询单条数据
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql = "select * from employee where dname = ?";
//查询复合数据
List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
public List<Map<String, Object>> findMapByDname(String dname){
String sql = "select eno as empno , salary as s from employee where dname = ?";
//将查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
public void insert(Employee employee){
String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
//利用update方法实现数据写入操作
jdbcTemplate.update(sql,new Object[]{
employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate()
});
}
public int update(Employee employee){
String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?";
int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
return count;
}
public int delete(Integer eno){
String sql = "delete from employee where eno = ?";
return jdbcTemplate.update(sql, new Object[]{eno});
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--JdbcTemplate提供数据CRUD的API-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
<!--为Dao注入JdbcTemplate对象-->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
<property name="employeeDao" ref="employeeDao"/>
<property name="batchService" ref="batchService"/>
</bean>
<bean id="batchService" class="com.imooc.spring.jdbc.service.BatchService">
<property name="employeeDao" ref="employeeDao"/>
</bean>
<!-- 1-3具体含义:当我们目标方法是imooc包下Service 且方法名为batchImport的时候 则认为当前方法需要使用事务-->
<!-- 利用transactionManager来完成对事务的提交以及回滚-->
<!-- 1.事务管理器,用于创建事务/提交/回滚 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务 advice是通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 propagation="REQUIRED"需要使用-->
<tx:method name="batchImport" propagation="REQUIRED"/>
<tx:method name="batch*" propagation="REQUIRED"/>
<!-- 设置所有findXXX方法不需要使用事务 -->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="importJob1" propagation="REQUIRES_NEW"/>
<tx:method name="importJob2" propagation="REQUIRES_NEW"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3. 定义声明式事务的作用范围-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
jdbcTemplateTestor.java
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import com.imooc.spring.jdbc.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private EmployeeDao employeeDao;
@Resource
private EmployeeService employeeService;
@Test
public void testFindById(){
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
@Test
public void testFindByDname(){
System.out.println(employeeDao.findByDname("市场部"));
}
@Test
public void testFindMapByDname(){
System.out.println(employeeDao.findMapByDname("研发部"));
}
@Test
public void testInsert(){
Employee employee = new Employee();
employee.setEno(8888);
employee.setEname("赵六");
employee.setSalary(6666f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
@Test
public void testUpdate(){
Employee employee = employeeDao.findById(8888);
employee.setSalary(employee.getSalary() + 1000);
int count = employeeDao.update(employee);
System.out.println("本次更新" + count + "条数据");
}
@Test
public void testDelete(){
int count = employeeDao.delete(8888);
System.out.println("本次删除" + count + "条数据");
}
@Test
public void testBatchImport(){
employeeService.batchImport();
System.out.println("批量导入成功");
}
@Test
public void testStartImportJob(){
employeeService.startImportJob();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc.spring</groupId>
<artifactId>jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--logback日志组件,Spring框架默认集成-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
</project>
注解配置声明式事务
//声明式事务核心注解
//放在类上,将声明式事务配置应用于当前类所有方法,默认事务传播为 REQUIRED
@Transactional(propagation = Propagation.REQUIRED) //不写也默认
写在类上就是所有
写在方法就是单独方法运用
com/imooc/spring/jdbc/dao/EmployeeDao.java
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@Repository
public class EmployeeDao {
@Resource
private JdbcTemplate jdbcTemplate;
public Employee findById(Integer eno){
String sql = "select * from employee where eno = ?";
//查询单条数据
Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
return employee;
}
public List<Employee> findByDname(String dname){
String sql = "select * from employee where dname = ?";
//查询复合数据
List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
return list;
}
public List<Map<String, Object>> findMapByDname(String dname){
String sql = "select eno as empno , salary as s from employee where dname = ?";
//将查询结果作为Map进行封装
List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
return maps;
}
public void insert(Employee employee){
String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
//利用update方法实现数据写入操作
jdbcTemplate.update(sql,new Object[]{
employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate()
});
}
public int update(Employee employee){
String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?";
int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
return count;
}
public int delete(Integer eno){
String sql = "delete from employee where eno = ?";
return jdbcTemplate.update(sql, new Object[]{eno});
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
com/imooc/spring/jdbc/entity/Employee.java
public class Employee {
private Integer eno;
private String ename;
private Float salary;
private String dname;
private Date hiredate;
} getter+setter
@Override
public String toString() {
return "Employee{" +
"eno=" + eno +
", ename='" + ename + '\'' +
", salary=" + salary +
", dname='" + dname + '\'' +
", hiredate=" + hiredate +
'}';
}
com/imooc/spring/jdbc/service/BatchService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import com.sun.xml.internal.ws.developer.Serialization;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
@Service
//不需要使用事务 只读操作
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class BatchService {
@Resource
private EmployeeDao employeeDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void importJob1(){
for (int i = 1; i <= 10; i++) {
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("研发部员工" + i);
employee.setSalary(4000f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void importJob2(){
for (int i = 1; i <= 10; i++) {
Employee employee = new Employee();
employee.setEno(9000 + i);
employee.setEname("市场部员工" + i);
employee.setSalary(4500f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
com/imooc/spring/jdbc/service/EmployeeService.java
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
@Service
//声明式事务核心注解
//放在类上,将声明式事务配置应用于当前类所有方法,默认事务传播为 REQUIRED
@Transactional(propagation = Propagation.REQUIRED) //不写也默认
public class EmployeeService {
@Resource
private EmployeeDao employeeDao;
@Resource
private BatchService batchService;
@Transactional(propagation = Propagation.NOT_SUPPORTED , readOnly = true)
public Employee findById(Integer eno){
return employeeDao.findById(eno);
}
public void batchImport() {
for (int i = 1; i <= 10; i++) {
if(i==3){
throw new RuntimeException("意料之外的异常");
}
Employee employee = new Employee();
employee.setEno(8000 + i);
employee.setEname("员工" + i);
employee.setSalary(4000f);
employee.setDname("市场部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
}
public void startImportJob(){
batchService.importJob1();
if(1==1){
throw new RuntimeException("意料之外的异常");
}
batchService.importJob2();
System.out.println("批量导入成功");
}
public EmployeeDao getEmployeeDao() {
return employeeDao;
}
public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
public BatchService getBatchService() {
return batchService;
}
public void setBatchService(BatchService batchService) {
this.batchService = batchService;
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.imooc"/>
<!--数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 启用注解形式声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc.spring</groupId>
<artifactId>jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--logback日志组件,Spring框架默认集成-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
</project>
test/java/JdbcTemplateTestor.java
import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import com.imooc.spring.jdbc.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private EmployeeDao employeeDao;
@Resource
private EmployeeService employeeService;
@Test
public void testFindById(){
Employee employee = employeeDao.findById(3308);
System.out.println(employee);
}
@Test
public void testFindByDname(){
System.out.println(employeeDao.findByDname("市场部"));
}
@Test
public void testFindMapByDname(){
System.out.println(employeeDao.findMapByDname("研发部"));
}
@Test
public void testInsert(){
Employee employee = new Employee();
employee.setEno(8888);
employee.setEname("赵六");
employee.setSalary(6666f);
employee.setDname("研发部");
employee.setHiredate(new Date());
employeeDao.insert(employee);
}
@Test
public void testUpdate(){
Employee employee = employeeDao.findById(8888);
employee.setSalary(employee.getSalary() + 1000);
int count = employeeDao.update(employee);
System.out.println("本次更新" + count + "条数据");
}
@Test
public void testDelete(){
int count = employeeDao.delete(8888);
System.out.println("本次删除" + count + "条数据");
}
@Test
public void testBatchImport(){
employeeService.batchImport();
System.out.println("批量导入成功");
}
@Test
public void testStartImportJob(){
employeeService.startImportJob();
}
}